package hs.framework.opcClient.controller;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import hs.framework.opcClient.entity.OPCServer;
import hs.framework.opcClient.framework.util.Condition;
import hs.framework.opcClient.framework.util.Func;
import hs.framework.opcClient.framework.util.Query;
import hs.framework.opcClient.framework.util.R;
import hs.framework.opcClient.service.IOPCServerService;
import hs.framework.opcClient.service.impl.OPCService;
import hs.framework.opcClient.utils.*;
import hs.framework.opcClient.wrapper.OPCServerWrapper;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.AllArgsConstructor;
import org.jinterop.dcom.common.JIException;
import org.jinterop.dcom.core.JIVariant;
import org.openscada.opc.lib.common.AlreadyConnectedException;
import org.openscada.opc.lib.common.NotConnectedException;
import org.openscada.opc.lib.da.*;
import org.openscada.opc.lib.da.browser.Branch;
import org.openscada.opc.lib.da.browser.FlatBrowser;
import org.openscada.opc.lib.da.browser.Leaf;
import org.openscada.opc.lib.da.browser.TreeBrowser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Description TODO
 * @Author sxmz
 * @Date 2021/5/11 0011 下午 4:40
 */
@CrossOrigin
@RestController
@AllArgsConstructor
@RequestMapping("/opcserver")
@Api(value = "opc服务器表,用于连接opc服务器", tags = "opc服务器表,用于连接opc服务器")
public class OPCServerController {
    @Autowired
    private IOPCServerService opcServerService;

    private static Logger log = LoggerFactory.getLogger(OPCController.class);
    /**
     * 详情
     */
    @GetMapping("/detail")
    @ApiOperationSupport(order = 1)
    @ApiOperation(value = "详情", notes = "传入OPCServer")
    public R<OPCServer> detail(OPCServer OPCServer) {
        OPCServer detail = opcServerService.getOne(Condition.getQueryWrapper(OPCServer));
        return R.data(OPCServerWrapper.build().entityVO(detail));
    }

    /**
     * 分页 数据源信息表,用于对接控制数据
     */
    @GetMapping("/list")
    @ApiOperationSupport(order = 2)
    @ApiOperation(value = "分页", notes = "传入OPCServer")
    public R<IPage<OPCServer>> list(OPCServer OPCServer, Query query) {
        IPage<OPCServer> pages = opcServerService.page(Condition.getPage(query), Condition.getQueryWrapper(OPCServer));
        return R.data(OPCServerWrapper.build().pageVO(pages));
    }


    /**
     * 自定义分页 数据源信息表,用于对接控制数据
     */
    @GetMapping("/page")
    @ApiOperationSupport(order = 3)
    @ApiOperation(value = "分页", notes = "传入OPCServer")
    public R<IPage<OPCServer>> page(OPCServer OPCServer, Query query) {
        IPage<OPCServer> pages = opcServerService.selectOPCServerPage(Condition.getPage(query), OPCServer);
        return R.data(pages);
    }

    /**
     * 新增 数据源信息表,用于对接控制数据
     */
    @PostMapping("/save")
    @ApiOperationSupport(order = 4)
    @ApiOperation(value = "新增", notes = "传入OPCServer")
    public R save(@Valid @RequestBody OPCServer OPCServer) {
        return R.status(opcServerService.save(OPCServer));
    }

    /**
     * 修改 数据源信息表,用于对接控制数据
     */
    @PostMapping("/update")
    @ApiOperationSupport(order = 5)
    @ApiOperation(value = "修改", notes = "传入OPCServer")
    public R update(@Valid @RequestBody OPCServer OPCServer) {
        return R.status(opcServerService.updateById(OPCServer));
    }

    /**
     * 新增或修改 数据源信息表,用于对接控制数据
     */
    @PostMapping("/submit")
    @ApiOperationSupport(order = 6)
    @ApiOperation(value = "新增或修改", notes = "传入OPCServer")
    public R submit(@Valid @RequestBody OPCServer OPCServer) {
        return R.status(opcServerService.saveOrUpdate(OPCServer));
    }


    /**
     * 删除 数据源信息表,用于对接控制数据
     */
    @PostMapping("/remove")
    @ApiOperationSupport(order = 8)
    @ApiOperation(value = "删除", notes = "传入ids")
    public R remove(@ApiParam(value = "主键集合", required = true) @RequestParam String ids) {
        return R.status(opcServerService.removeByIds(Func.toLongList(ids)));
    }

    /**
     * 获取所有已连接的Server
     */
    @GetMapping("/getConnectServer")
    @ApiOperationSupport(order = 9)
    @ApiOperation(value = "获取所有已连接的Server")
    public R<List<Server>> getConnectServer(){
        return R.data(opcServerService.getConnectServer());
    }

    /**
     * 删除 数据源信息表,用于对接控制数据
     */
    @GetMapping("/disposeServer")
    @ApiOperationSupport(order = 10)
    @ApiOperation(value = "断开opc服务器连接", notes = "传入opc服务器")
    public R disposeServer(OPCServer opcServer){
        opcServer = opcServerService.getOne(Condition.getQueryWrapper(opcServer));
        Boolean flag = opcServerService.disposeServer(opcServer);
        return flag ? R.success("操作成功！服务器已断开连接..."):R.fail("操作失败！请检查服务器是否在连接状态！不要重复断开连接！");
    }

    @GetMapping("items")
    public R<List<String>> getAll(OPCServer opcServer) throws AlreadyConnectedException, JIException, UnknownHostException {
        Server server = getServer(opcServer).getData();
        List<String> result = new ArrayList<>();
        try {
            FlatBrowser fb = server.getFlatBrowser();
            TreeBrowser tb = server.getTreeBrowser();

            if (fb!=null && fb.browse()!=null) {
                Collection<String> itemIds = fb.browse();
                result = itemIds.parallelStream().collect(Collectors.toList());
            }
            //树形结构解析
            else if (tb!=null && tb.browse()!=null) {
                Branch b = tb.browse();
                setTree(b,result);
            }

            return R.data(result);
        } catch (final JIException e) {
            OPCService.disposeServer();
            log.error(e.getMessage());
            log.error(String.format("%08X: %s", e.getErrorCode(), server.getErrorMessage(e.getErrorCode())));
        } catch (UnknownHostException e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
        return R.fail("获取item列表失败！请检查服务器是否连接成功！");
    }

    //
    private void setTree(Branch b, List<String> list) {
        Collection<Branch> bList = b.getBranches();
        Collection<Leaf> lList = b.getLeaves();

        if (bList!=null && bList.size()>0) {
            for (Branch b1 : bList) {
                setTree(b1, list);
            }
        }else if (lList!=null && lList.size()>0) {
            for (Leaf l1 : lList) {
                list.add(l1.getItemId());
            }
        }
    }

    @GetMapping("getServer")
    public R<Server> getServer(OPCServer opcServer) throws AlreadyConnectedException, JIException, UnknownHostException {
        opcServer = opcServerService.getOne(Condition.getQueryWrapper(opcServer));
        Server server = opcServerService.openServer(opcServer);
        return R.data(server);
    }

    @GetMapping("dataitem")
    public R<ListResult<DataItem>> getDataItemsFromServer(OPCServer opcServer,@RequestParam List<String> itemId) throws AlreadyConnectedException, JIException, UnknownHostException {
        Server server = getServer(opcServer).getData();
        Collection<String> itemIds = itemId;
        ListResult<DataItem> dataItems = ServerUtil.readSync(server,itemIds);
        return R.data(dataItems);
    }

    @GetMapping("dataitems")
    public R<ListResult<DataItem>> getDataItemsFromServer(OPCServer opcServer) throws AlreadyConnectedException, JIException, UnknownHostException {
        final Server server = getServer(opcServer).getData();
        ListResult<DataItem> dataItems = ServerUtil.readSync(server);
        return R.data(dataItems);
    }

    @PutMapping("write")
    public R<ListResult<DataItem>> change(OPCServer opcServer, @RequestBody List<DataItem> dataItems) throws Exception {
        final Server server = getServer(opcServer).getData();
        ListResult<DataItem> result = ServerUtil.writeValue(server,dataItems);
        return R.data(result);
    }


}
